import json
import time

import jwt
import httplib2
import simplejson

import coin.exchange.base.kr_rest.rest_client_base as rcb
from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin.exchange.util.rate_limit import RateLimiter
from coin.exchange.util.order_checker import OrderAmountChecker


class UpbitOrderBase(object):
  def __init__(self, key_file, proxy=None):
    key = AuthKey.from_file(key_file)
    self.access_token = key.access_key
    self.secret_key = key.secret_key_bytes
    self.refresh_token = key.refresh_token
    self.rate_limit = RateLimiter(3, 1.0)
    self.api_url = 'https://api.upbit.com/v1'
    self._proxy = proxy

  def _nonce(self):
    return str(int(round(time.time() * 1000000)))

  def get_auth(self):
    encoded = jwt.encode({
        'access_key': self.access_token, "nonce": self._nonce()
    },
                         self.secret_key,
                         algorithm='HS256')
    return "Bearer %s" % (encoded.decode("utf-8"))

  def get_response(self, url, request_type, payload):
    headers = {
        'Content-type': 'application/json',
        'Authorization': self.get_auth(),
        'Cache-Control': 'no-cache',
    }
    encoded_payload = json.dumps(payload).encode("utf-8")
    proxy = self._proxy
    if proxy is not None:
      pi = httplib2.proxy_info_from_url(proxy['http'])
      http = httplib2.Http(proxy_info=pi)
    else:
      http = httplib2.Http()
    response, content = http.request(self.api_url + "/" + url,
                                     request_type,
                                     headers=headers,
                                     body=encoded_payload)
    return content, response

  def _query_nocheck(self, endpoint, request_type, params=None):
    if params:
      params = {k: v for k, v in params.items() if v is not None}
    req_timestamp = time.time()
    content, header = self.get_response(endpoint, request_type, params)
    res_timestamp = time.time()
    content = simplejson.loads(content)
    return rcb.RestUpdate(req_timestamp, res_timestamp, content, native_header=header)

  def _query(self, endpoint, request_type, params=None, disable_rate_limit=False):
    if not disable_rate_limit:
      while not self.rate_limit.check():
        time.sleep(0.1)
    else:
      self.rate_limit.check()
    return self._query_nocheck(endpoint, request_type, params=params)


class UpbitNativePrivateClient(UpbitOrderBase):
  def __init__(self, key_file, proxy=None):
    UpbitOrderBase.__init__(self, key_file, proxy)
    self.order_amount_checker = OrderAmountChecker("KRW", 500, 50000000)

  def query_GET_accounts(self):
    update = self._query("accounts", "GET")
    return update

  def query_GET_order(self, *, uuid=None, identifier=None):
    update = self._query("order", "GET", params=dict(
        uuid=uuid,
        identifier=identifier,
    ))
    return update

  def query_GET_orders(self, *, market=None, state=None, page=None, order_by=None):
    update = self._query("orders",
                         "GET",
                         params=dict(
                             market=market,
                             state=state,
                             page=page,
                             order_by=order_by,
                         ))
    return update

  def query_POST_orders(self,
                        *,
                        market=None,
                        side=None,
                        volume=None,
                        price=None,
                        ord_type=None,
                        identifier=None):
    update = self._query("orders",
                         "POST",
                         params=dict(
                             market=market,
                             side=side,
                             volume=volume,
                             price=price,
                             ord_type=ord_type,
                             identifier=identifier,
                         ))
    return update

  def query_DELETE_order(self, *, uuid=None):
    update = self._query("order", "DELETE", params=dict(uuid=uuid,))
    return update
